package net.entelijan.cobean.bind.impl;

import static org.junit.Assert.assertEquals;
import net.entelijan.cobean.bind.impl.BoundPropBinding;
import net.entelijan.cobean.core.ICobean;
import net.entelijan.cobean.text.DefaultTextInitializer;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.fest.swing.fixture.FrameFixture;
import org.fest.swing.fixture.JTextComponentFixture;
import org.junit.Test;

public class PathBindingTestCase {

    private static Log log = LogFactory.getLog(PathBindingTestCase.class);

    NestedABean abean;
    INestedBBean bbean;

    @Test
    public void testABeanAlwaysNewValue() {
        log.debug("[testABeanAlwasNewValue]");
        abean = new NestedABean("start", null);

        PathBindingFrame frame = new PathBindingFrame();
        FrameFixture frameFix = new FrameFixture(frame);
        try {
            frameFix.show();
            JTextComponentFixture inputTextBox = frameFix.textBox("inputTextField");
            JTextComponentFixture dummyTextBox = frameFix.textBox("dummyTextField");

            initABean(frame);
            log.debug("initialized");
            inputTextBox.requireText("");

            abean.setBeanVal(new NestedABean("val1", null));
            log.debug("set bean 'val1'");
            pause(20);
            inputTextBox.requireText("val1");

            abean.getBeanVal().setStrVal("val2");
            pause(20);
            log.debug("set bean 'val2'");
            pause(20);
            inputTextBox.requireText("val2");

            abean.setBeanVal(new NestedABean("val3", null));
            log.debug("set bean 'val3'");
            pause(20);
            inputTextBox.requireText("val3");

            inputTextBox.setText("val4");
            dummyTextBox.focus();
            log.debug("set comp 'val4'");
            pause(100);
            assertEquals("val4", abean.getBeanVal().getStrVal());

        } finally {
            frameFix.cleanUp();
        }
    }

    @Test
    public void testABeanTheSameAgainUnwrapped() {
        log.debug("[testABeanTheSameAgainUnwrapped]");
        abean = new NestedABean("start", null);

        PathBindingFrame frame = new PathBindingFrame();
        FrameFixture frameFix = new FrameFixture(frame);
        try {
            frameFix.show();
            JTextComponentFixture inputTextBox = frameFix.textBox("inputTextField");

            initABean(frame);
            log.debug("initialized");
            inputTextBox.requireText("");

            abean.setBeanVal(new NestedABean("val1", null));
            pause(20);
            inputTextBox.requireText("val1");

            abean.setBeanVal(new NestedABean("val3", null));
            pause(20);
            inputTextBox.requireText("val3");

            abean.setBeanVal(new NestedABean("val1", null));
            pause(20);
            inputTextBox.requireText("val1");

        } finally {
            frameFix.cleanUp();
        }
    }

    @Test
    public void testABeanTheSameAgainWrapped() {
        log.debug("[testABeanTheSameAgainWrapped]");
        abean = new NestedABean("start", null);

        PathBindingFrame frame = new PathBindingFrame();
        FrameFixture frameFix = new FrameFixture(frame);
        try {
            frameFix.show();
            JTextComponentFixture inputTextBox = frameFix.textBox("inputTextField");

            initABean(frame);
            log.debug("initialized");
            inputTextBox.requireText("");

            NestedABean bean1 = new NestedABean("val1", null);
            abean.setBeanVal(bean1);
            bean1 = abean.getBeanVal();
            pause(20);
            inputTextBox.requireText("val1");

            assertEquals("val1", bean1.getStrVal());
            abean.setBeanVal(new NestedABean("val3", null));
            assertEquals("'bean1' was changed by setting a new bean to the base bean.", "val1",
                    bean1.getStrVal());
            pause(20);
            inputTextBox.requireText("val3");

        } finally {
            frameFix.cleanUp();
        }
    }

    @Test
    public void testBBeanAlwaysNewValue() {
        log.debug("[testBBeanAlwaysNewValue]");
        bbean = new NestedBBean("start", null);

        PathBindingFrame frame = new PathBindingFrame();
        FrameFixture frameFix = new FrameFixture(frame);
        try {
            frameFix.show();
            JTextComponentFixture inputTextBox = frameFix.textBox("inputTextField");
            JTextComponentFixture dummyTextBox = frameFix.textBox("dummyTextField");

            initBBean(frame);
            log.debug("initialized");
            inputTextBox.requireText("");

            bbean.setBeanVal(new NestedBBean("val1", null));
            log.debug("set bean 'val1'");
            pause(20);
            inputTextBox.requireText("val1");

            bbean.getBeanVal().setStrVal("val2");
            pause(20);
            log.debug("set bean 'val2'");
            pause(20);
            inputTextBox.requireText("val2");

            bbean.setBeanVal(new NestedBBean("val3", null));
            log.debug("set bean 'val3'");
            pause(20);
            inputTextBox.requireText("val3");

            inputTextBox.setText("val4");
            dummyTextBox.focus();
            log.debug("set comp 'val4'");
            pause(20);
            assertEquals("val4", bbean.getBeanVal().getStrVal());

        } finally {
            frameFix.cleanUp();
        }
    }

    @Test
    public void testBBeanTheSameAgainUnwrapped() {
        log.debug("[testBBeanTheSameAgainUnwrapped]");
        bbean = new NestedBBean("start", null);

        PathBindingFrame frame = new PathBindingFrame();
        FrameFixture frameFix = new FrameFixture(frame);
        try {
            frameFix.show();
            JTextComponentFixture inputTextBox = frameFix.textBox("inputTextField");

            initBBean(frame);
            log.debug("initialized");
            inputTextBox.requireText("");

            bbean.setBeanVal(new NestedBBean("val1", null));
            pause(20);
            inputTextBox.requireText("val1");

            bbean.setBeanVal(new NestedBBean("val3", null));
            pause(20);
            inputTextBox.requireText("val3");

            bbean.setBeanVal(new NestedBBean("val1", null));
            pause(20);
            inputTextBox.requireText("val1");

        } finally {
            frameFix.cleanUp();
        }
    }

    @Test
    public void testBBeanTheSameAgainWrapped() {
        log.debug("[testBBeanTheSameAgainWrapped]");
        bbean = new NestedBBean("start", null);

        PathBindingFrame frame = new PathBindingFrame();
        FrameFixture frameFix = new FrameFixture(frame);
        try {
            frameFix.show();
            JTextComponentFixture inputTextBox = frameFix.textBox("inputTextField");

            initBBean(frame);
            log.debug("initialized");
            inputTextBox.requireText("");

            INestedBBean bean1 = new NestedBBean("val1", null);
            bbean.setBeanVal(bean1);
            bean1 = bbean.getBeanVal();
            pause(20);
            inputTextBox.requireText("val1");

            assertEquals("val1", bean1.getStrVal());
            bbean.setBeanVal(new NestedBBean("val3", null));
            assertEquals("'bean1' was changed by setting a new bean to the base bean.", "val1",
                    bean1.getStrVal());
            pause(20);
            inputTextBox.requireText("val3");

        } finally {
            frameFix.cleanUp();
        }
    }

    @Test  
    public void testDepth3_01() {
        log.debug("[testABeanAlwaysNewValueDepth3]");
        abean = new NestedABean("start", null);

        PathBindingFrame frame = new PathBindingFrame();
        FrameFixture frameFix = new FrameFixture(frame);
        try {
            frameFix.show();
            JTextComponentFixture inputTextBox = frameFix.textBox("inputTextField");
            JTextComponentFixture dummyTextBox = frameFix.textBox("dummyTextField");

            log.debug("------------------ initABeanDepth3(frame);");
            initABeanDepth3(frame);
            inputTextBox.requireText("");

            log.debug("------------------ abean.setBeanVal(new NestedABean(\"val11\", new NestedABean(\"val12\", null)));");
            abean.setBeanVal(new NestedABean("val11", new NestedABean("val12", null)));
            pause(20);
            inputTextBox.requireText("val12");

            log.debug("------------------ abean.getBeanVal().getBeanVal().setStrVal(\"val22\")");
            abean.getBeanVal().getBeanVal().setStrVal("val22");
            pause(20);
            inputTextBox.requireText("val22");

            log.debug("------------------ abean.getBeanVal().setBeanVal(new NestedABean(\"val32\", null));");
            abean.getBeanVal().setBeanVal(new NestedABean("val32", null));
            pause(20);
            pause(20);
            inputTextBox.requireText("val32");

            log.debug("------------------ abean.setBeanVal(new NestedABean(\"va41\", new NestedABean(\"val42\", null)));");
            abean.setBeanVal(new NestedABean("va41", new NestedABean("val42", null)));
            pause(20);

            inputTextBox.setText("val5");
            dummyTextBox.focus();
            pause(100);
            assertEquals("val5", abean.getBeanVal().getBeanVal().getStrVal());

        } finally {
            frameFix.cleanUp();
        }
    }

    @Test 
    public void testDepth3_02() {
        log.debug("[testABeanTheSameAgainUnwrappedDepth3]");
        abean = new NestedABean("start", null);

        PathBindingFrame frame = new PathBindingFrame();
        FrameFixture frameFix = new FrameFixture(frame);
        try {
            frameFix.show();
            JTextComponentFixture inputTextBox = frameFix.textBox("inputTextField");

            initABeanDepth3(frame);
            log.debug("initialized");
            inputTextBox.requireText("");

            abean.setBeanVal(new NestedABean("val11", new NestedABean("val12", null)));
            pause(200);
            inputTextBox.requireText("val12");

            abean.setBeanVal(new NestedABean("va21", new NestedABean("val22", null)));
            pause(20);
            inputTextBox.requireText("val22");

            abean.setBeanVal(new NestedABean("val11", new NestedABean("val12", null)));
            pause(20);
            inputTextBox.requireText("val12");

        } finally {
            frameFix.cleanUp();
        }
    }

    @Test 
    public void testDepth3_04() {
        log.debug("[testABeanTheSameAgainWrapped]");
        abean = new NestedABean("start", null);

        PathBindingFrame frame = new PathBindingFrame();
        FrameFixture frameFix = new FrameFixture(frame);
        try {
            frameFix.show();
            JTextComponentFixture inputTextBox = frameFix.textBox("inputTextField");

            log.debug("-------- before initABeanDepth3(frame) -----------");
            initABeanDepth3(frame);
            log.debug("-------- after initABeanDepth3(frame) -----------");
            inputTextBox.requireText("");

            NestedABean bean1 = new NestedABean("val11", new NestedABean("val12", null));
            log.debug("-------- before abean.setBeanVal(bean1) -----------");
            abean.setBeanVal(bean1);
            log.debug("-------- after abean.setBeanVal(bean1) -----------");
            
            bean1 = abean.getBeanVal();
            pause(20);
            inputTextBox.requireText("val12");

            assertEquals("val12", bean1.getBeanVal().getStrVal());
            assertEquals("val11", bean1.getStrVal());
            log.debug("-------- before abean.setBeanVal(new NestedABean(\"val31\", new NestedABean(\"val32\", null))) -----------");
            abean.setBeanVal(new NestedABean("val31", new NestedABean("val32", null)));
            log.debug("-------- after abean.setBeanVal(new NestedABean(\"val31\", new NestedABean(\"val32\", null))) -----------");
            assertEquals("val12", bean1.getBeanVal().getStrVal());
            assertEquals("val11", bean1.getStrVal());
            pause(20);
            inputTextBox.requireText("val32");

        } finally {
            frameFix.cleanUp();
        }
    }

    @Test 
    public void testDepth3_05() {
        log.debug("[testABeanTheSameAgainWrapped]");
        abean = new NestedABean("start", new NestedABean("start1", new NestedABean("start2", null)));

        PathBindingFrame frame = new PathBindingFrame();
        FrameFixture frameFix = new FrameFixture(frame);
        try {
            frameFix.show();
            JTextComponentFixture inputTextBox = frameFix.textBox("inputTextField");

            log.debug("-------- before initABeanDepth3(frame) -----------");
            initABeanDepth3(frame);
            log.debug("-------- after initABeanDepth3(frame) -----------");
            inputTextBox.requireText("start2");

            NestedABean bean1 = new NestedABean("val11", new NestedABean("val12", null));
            log.debug("-------- before abean.setBeanVal(bean1) -----------");
            abean.setBeanVal(bean1);
            log.debug("-------- after abean.setBeanVal(bean1) -----------");
            
            bean1 = abean.getBeanVal();
            pause(20);
            inputTextBox.requireText("val12");

            assertEquals("val12", bean1.getBeanVal().getStrVal());
            assertEquals("val11", bean1.getStrVal());
            log.debug("-------- before abean.setBeanVal(new NestedABean(\"val31\", new NestedABean(\"val32\", null))) -----------");
            abean.setBeanVal(new NestedABean("val31", new NestedABean("val32", null)));
            log.debug("-------- after abean.setBeanVal(new NestedABean(\"val31\", new NestedABean(\"val32\", null))) -----------");
            assertEquals("val12", bean1.getBeanVal().getStrVal());
            assertEquals("val11", bean1.getStrVal());
            pause(20);
            inputTextBox.requireText("val32");

        } finally {
            frameFix.cleanUp();
        }
    }

    private void initABean(PathBindingFrame frame) {
        DefaultTextInitializer<String> ini = new DefaultTextInitializer<String>();
        ini.setCoTextComponent(frame.getInputTextField());
        ICobean<String> cobean = ini.initialize();

        BoundPropBinding bpb = new BoundPropBinding();
        bpb.setModelPath("beanVal.strVal");
        bpb.setCompProperty("value");

        abean = bpb.bind(abean, cobean);
    }

    private void initABeanDepth3(PathBindingFrame frame) {
        DefaultTextInitializer<String> ini = new DefaultTextInitializer<String>();
        ini.setCoTextComponent(frame.getInputTextField());
        ICobean<String> cobean = ini.initialize();

        BoundPropBinding bpb = new BoundPropBinding();
        bpb.setModelPath("beanVal.beanVal.strVal");
        bpb.setCompProperty("value");

        abean = bpb.bind(abean, cobean);
    }

    private void initBBean(PathBindingFrame frame) {
        DefaultTextInitializer<String> ini = new DefaultTextInitializer<String>();
        ini.setCoTextComponent(frame.getInputTextField());
        ICobean<String> cobean = ini.initialize();

        BoundPropBinding bpb = new BoundPropBinding();
        bpb.setModelPath("beanVal.strVal");
        bpb.setCompProperty("value");
        bbean = bpb.bind(bbean, cobean);
    }

    private synchronized void pause(int ms) {
        try {
            this.wait(ms);
        } catch (InterruptedException e) {
            // Can be ignored
        }
    }

}
